package com.bemes.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import com.bemes.ecmn.model.*;


public class ECMNXmlParse {
	
	private ArrayList<ECMNGenericObject> m_objects;
	private ECMNGenericObject m_rootEq;
	
	private static final String ROOT_EQUIPMENT = "rootequipment";
	private static final String EQUIPMENT = "equipment";
	private static final String NAME = "name";
	private static final String ID = "id";
	
	private static final String DRIVER = "driver";
	private static final String INPUT = "input";
	
	private static final String CONNECTOR = "connector";
	private static final String CHILD_ID = "childId";
	private static final String PARENT_ID = "parentId";	
	private static final String PGRANULARITY = "pgranularity";
	private static final String CGRANULARITY = "cgranularity";
	
	private static final String AGGREGATOR = "aggregator";
	private static final String AGGR_TYPE = "aggrtype";
	
	public ECMNXmlParse()
	{
		m_objects = new ArrayList<ECMNGenericObject>();
		m_rootEq = null;
	}
	
	private void makeConnector(String id, String name, int pGran, int cGran, String pId, String cId)
	{
		ECMNGenericObject childObj = null;
		ECMNGenericObject parentObj = null;
		
		for(ECMNGenericObject go : m_objects)
		{
			if(childObj != null && parentObj != null)
			{
				break;
			}
			
			if(go.getId().equals(pId))
			{
				parentObj = go;
				continue;
			}
			
			if(go.getId().equals(cId))
			{
				childObj = go;
				continue;
			}			
		}
		
		if(parentObj != null && childObj != null)
		{		
			if(parentObj.getObjectType() != ECMNEnum.COMPOSITION_OBJECT)
			{
				//parent object must be subclass of ECMNCompositionObject class
				return;
			}
			
			if(childObj.getObjectType() != ECMNEnum.COMPOSITION_OBJECT && childObj.getObjectType() != ECMNEnum.LINKABLE_OBJECT)
			{
				//child object must be subclass of ECMNCompositionObject or ECMNLinkableObject class
				return;
			}
			
			ECMNConnector con = new ECMNConnector();
			con.setId(id);
			con.setName(name);
			con.SetGranularity(pGran, cGran);
			con.makeLink((ECMNCompositionObject)parentObj, (ECMNLinkableObject)childObj);
			
			m_objects.add(con);
		}
	}
	
	private void parseSingleConnetorNode(Node connector, String id, String name, String pG, String cG)
	{
		int pGran, cGran;
		
		try{
			pGran = Integer.valueOf(pG);
			cGran = Integer.valueOf(cG);
		} catch (NumberFormatException e){
			// use default granularity (1:1)
			pGran = 1;
			cGran = 1;
		}
		
		String childId = "";
		String parentId = "";
		
		NodeList childNodes = connector.getChildNodes();
		for(int i = 0; i < childNodes.getLength(); i++)
		{
			Node aNode = childNodes.item(i);
			if(aNode.getNodeName() == CHILD_ID)
			{
				childId = aNode.getTextContent();
			} else if(aNode.getNodeName() == PARENT_ID)
			{
				parentId = aNode.getTextContent();
			}
		}
		
		if(childId.equals("") || parentId.equals(""))
		{
			// this connector does not contains enough information. Return.
			return;
		}
		else
		{
			makeConnector(id, name, pGran, cGran, parentId, childId);
		}
	}
	
	private void parseConnectorNodeList(NodeList connectors)
	{
		for(int i = 0; i < connectors.getLength(); i++)
		{
			Node cNode = connectors.item(i);
			NamedNodeMap attributes = cNode.getAttributes();
			if(attributes != null)
			{
				String strNodeId = "";
				String strNodeName = "";
				String pgranularity = "";
				String cgranularity = "";
				
				for(int j = 0; j < attributes.getLength(); j++)
				{
					Node aNode = attributes.item(j);
					String nodeName = aNode.getNodeName();
					String nodeValue = aNode.getNodeValue();
					
					if(nodeName.equals(ID)) {
						strNodeId = nodeValue;
					} else if (nodeName.equals(NAME)) {
						strNodeName = nodeValue;
					} else if (nodeName.equals(PGRANULARITY)){
						pgranularity = nodeValue;
					} else if (nodeName.equals(CGRANULARITY)){
						cgranularity = nodeValue;
					}
				}
				
				parseSingleConnetorNode(cNode, strNodeId, strNodeName, pgranularity, cgranularity);
			}
		}
	}
	
	private void parseSingleDriverNode(Node driver)
	{
		NamedNodeMap attributes = driver.getAttributes();
		if(attributes != null)
		{
			String strNodeId = "";
			String strNodeName = "";
			String localInput = "";
			
			for(int j = 0; j < attributes.getLength(); j++)
			{
				Node aNode = attributes.item(j);
				String nodeName = aNode.getNodeName();
				String nodeValue = aNode.getNodeValue();
				
				if(nodeName.equals(ID)) {
					strNodeId = nodeValue;
				} else if (nodeName.equals(NAME)) {
					strNodeName = nodeValue;
				}
			}
			
			if(strNodeId.isEmpty() == false)
			{
				NodeList drvChilds = driver.getChildNodes();
				for(int i = 0; i < drvChilds.getLength(); i++)
				{
					if(drvChilds.item(i).getNodeName().equals("input"))
					{
						localInput = drvChilds.item(i).getTextContent();
						break;
					}
				}
				
				System.out.println("LocalInput = " + localInput);
				
				int driverInput;
				try{
					driverInput = Integer.valueOf(localInput);
				} catch (NumberFormatException e){
					driverInput = 0;
				}
				
				ECMNDriver dr = new ECMNDriver();
				dr.setId(strNodeId);
				dr.setName(strNodeName);
				dr.setAmountOfInput(driverInput);					
				m_objects.add(dr);
			}
		}
	}
	
	private void parseDriverNodeList(NodeList drivers)
	{
		for(int i = 0; i < drivers.getLength(); i++)
		{
			Node dNode = drivers.item(i);
			parseSingleDriverNode(dNode);
		}
	}
	
	private boolean parseSingleEquipmentNode(Node eqNode)
	{
		NamedNodeMap attributes = eqNode.getAttributes();
		if(attributes != null)
		{
			String strNodeId = "";
			String strNodeName = "";
			
			for(int j = 0; j < attributes.getLength(); j++)
			{
				Node aNode = attributes.item(j);
				String nodeName = aNode.getNodeName();
				String nodeValue = aNode.getNodeValue();
				
				if(nodeName.equals(ID)) {
					strNodeId = nodeValue;
				}
				else if (nodeName.equals(NAME)) {
					strNodeName = nodeValue;
				}				
			}
			
			if(strNodeId.isEmpty() == false)
			{
				ECMNEquipment eq = new ECMNEquipment();
				eq.setId(strNodeId);
				eq.setName(strNodeName);
				m_objects.add(eq);
				
				return true;
			}
		}
		
		return false;
	}
	
	private void parseEquimentNodeList(NodeList equipments)
	{
		for(int i = 0; i < equipments.getLength(); i++)
		{
			Node eNode = equipments.item(i);
			parseSingleEquipmentNode(eNode);
		}
	}
	
	private boolean parseRootEquiment(Node rootEq)
	{
		boolean result = parseSingleEquipmentNode(rootEq);
		if(result)
		{			
			if(m_objects.isEmpty() == false)
			{
				//root node is the first node in m_objects
				m_rootEq = m_objects.get(0);			
				return true;
			}
		}
		
		return false;
	}
		
	
	private ECMNAggregator produceAggregator(String strType)
	{
		/* Temporarily used for the first version.
		 * next version, aggregator should be produced by Factory class
		 */
		
		/*
	     * Sum aggregator : 1
	     * Mul aggregator : 2
	     * Dif aggregator : 3
	     * Min aggregator : 4
	     * Max aggregator : 5
	     * Avg aggregator : 6
	     * 
	     * Default type : 1
		 */
		
		ECMNAggregator aggr = null;
		int intAggrtype;
		try {
			intAggrtype = Integer.valueOf(strType);
		} catch (NumberFormatException e) {
			//Use default type
			intAggrtype = 1;
		}
		
		switch (intAggrtype)
		{
			case 2:
				aggr = new ECMNMulAggregator();
				break;
			case 3:
				aggr = new ECMNDifAggregator();
				break;
			case 4:
				aggr = new ECMNMinAggregator();
				break;
			case 5:
				aggr = new ECMNMaxAggregator();
				break;
			case 6:
				aggr = new ECMNAvgAggregator();
				break;
			default:
				aggr = new ECMNSumAggregator();
		}
		
		return aggr;
	}
	
	private void makeAggregator(String id, String name, String type)
	{
		ECMNAggregator aggr = produceAggregator(type);
		
		if(aggr != null)
		{
			aggr.setId(id);
			aggr.setName(name);
			m_objects.add(aggr);
		}
	}
	
	private void parseSingleAggregatorNode(Node aggregator)
	{
		NamedNodeMap attributes = aggregator.getAttributes();
		if(attributes != null)
		{
			String strNodeId = "";
			String strNodeName = "";
			String strAggrType = "";
			
			for(int j = 0; j < attributes.getLength(); j++)
			{
				Node aNode = attributes.item(j);
				String nodeName = aNode.getNodeName();
				String nodeValue = aNode.getNodeValue();
				
				if(nodeName.equals(ID)) {
					strNodeId = nodeValue;
				}
				else if (nodeName.equals(NAME)) {
					strNodeName = nodeValue;
				} else if (nodeName.equals(AGGR_TYPE)) {
					strAggrType = nodeValue;
				}
			}
			
			if(strNodeId.isEmpty() == false)
			{
				makeAggregator(strNodeId, strNodeName, strAggrType);
			}
		}
	}
	
	private void parseAggregatorNodeList(NodeList aggregators)
	{
		for(int i = 0; i < aggregators.getLength(); i++)
		{
			Node aNode = aggregators.item(i);
			parseSingleAggregatorNode(aNode);
		}
	}
	
	public ECMNGenericObject parse(String xmlContent)
	{
		m_objects.clear();
		m_rootEq = null;
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    DocumentBuilder builder = null;
	    InputStream  in = null;	    
	    
		try {
			builder = factory.newDocumentBuilder();

			//Here we do the actual parsing
			in = new ByteArrayInputStream(xmlContent.getBytes());
			Document doc = builder.parse(in);
			Element rootElement = doc.getDocumentElement();
			
			NodeList rootEq = rootElement.getElementsByTagName(ROOT_EQUIPMENT);
			//Only process if we find root equipment in the xmldoc
			if(rootEq.getLength() > 0)
			{
				boolean addedRoot = parseRootEquiment(rootEq.item(0));
				
				//only continue processing if rootEq is added successfully
				if(addedRoot)
				{
					// Process <equipment> item
					NodeList equipments = rootElement.getElementsByTagName(EQUIPMENT);
					parseEquimentNodeList(equipments);
					
					// Process <driver> item
					NodeList drivers = rootElement.getElementsByTagName(DRIVER);
					parseDriverNodeList(drivers);					
					
					// Process <aggregator> item
					NodeList aggregators = rootElement.getElementsByTagName(AGGREGATOR);
					parseAggregatorNodeList(aggregators);
					
					// Process <connector> item
					NodeList connectors = rootElement.getElementsByTagName(CONNECTOR);
					parseConnectorNodeList(connectors);
					
					/*
					for(ECMNGenericObject go : m_objects)
					{
						System.out.println(go.getObjectType());
						System.out.println(go.getName() + " : " + go.getId());
					}
					*/
				}
			}
			
	    } catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
	    	if(in != null)
	    	{
	    		try {
	    			in.close();
	    		} catch (Exception e){}
	    	}
	    }
		
		return m_rootEq;
	}
}
